﻿using System;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;
using GSensorSDK;

namespace Blue_Rogue_Remote
{
    public partial class MainForm : Form
    {
        delegate void DelegateForUIUpdate(double turn, double speed);

        #region Parameters
        private static int SCALE_FACTOR = 100;
        private static int SENSITIVITY = 2;
        #endregion

        #region Fields
        private int offX = 0, offY = 0;
        private int offsetY, offsetX;
        private bool _gSensorEnable;
        private HTCGSensor _gSensor;
        private GVector _gOffset;
        private GVector _movement;

        RobotController _robotController;
        #endregion

        #region Ctor
        /// <summary>
        /// Ctor - initializes all components
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
            _robotController = new RobotController();

            offsetY = (picPanel.Size.Height) / 16;
            offsetX = (picPanel.Size.Width) / 16;
            _gSensorEnable = false;
            
            InitCombobox();
            InitButtons();
            DrawBG();
            InitBall();
            
            InitGSensor();

            tabPage.SelectedIndex = 0;
            timer1.Tick += new EventHandler(timer1_Tick);
            
        }
        #endregion

        #region init fuctions
        /// <summary>
        /// Init GSensor
        /// </summary>
        private void InitGSensor()
        {
            _gSensor = new HTCGSensor();
            _movement = new GVector();
        }

        /// <summary>
        /// Fills up combobox with availible COM ports, and chooses
        /// the one hardcoded i BTController class
        /// </summary>
        private void InitCombobox()
        {
            foreach (string s in _robotController.GetPortNames())
            {
                cbComPort.Items.Add(s);
                if (s.CompareTo(_robotController.PortName) == 0)
                    cbComPort.SelectedItem = s;
            }
        }

        /// <summary>
        /// Init button listeners
        /// </summary>
        private void InitButtons()
        {
            btnCom.Click += new EventHandler(btnCom_Click);
            btnGSensor.Click += new EventHandler(btnGSensor_Click);
        }

        /// <summary>
        /// Init ball picture, and inits mouse up and down events on the picture
        /// </summary>
        private void InitBall()
        {
            ResourceManager resourceManager = new ResourceManager("Blue_Rogue_Remote.Resources", GetType().Assembly);
            Bitmap bmp = (Bitmap)resourceManager.GetObject("ball1");

            picBall.Height = bmp.Height;
            picBall.Width = bmp.Width;

            picBall.Image = bmp;
            picBall.Left = (picPanel.Size.Width / 2) - (picBall.Width / 2) + picPanel.Left;
            picBall.Top = (picPanel.Size.Height / 2) - (picBall.Height / 2) + picPanel.Top;

            picBall.MouseDown += new MouseEventHandler(picBall_MouseDown);
            picBall.MouseUp += new MouseEventHandler(picBall_MouseUp);

        }

        #endregion

        #region members
        /// <summary>
        /// Reads G sensor values
        /// Include simple lowpass filtering of input to smooth the movement
        /// </summary>
        private void ReadGSensor()
        {
            //Read new sensor values
            GVector gVector = _gSensor.GetGVector();
            gVector.Scale(SCALE_FACTOR);

            //Calc new position
            double x = ((_gOffset.X - gVector.X) * SENSITIVITY);
            double y = ((_gOffset.Y - gVector.Y) * SENSITIVITY);
            
            //lowpass filtering
            _movement.X = (0.75 * _movement.X) + 0.25 * x;
            _movement.Y = (0.75 * _movement.Y) + 0.25 * y;
                                
        }


        /// <summary>
        /// Reads mouse position compared to picPanel
        /// Scales the values to x/16 of the size of picPanel
        /// to get two nibles, one for forward and back and one for turn
        /// </summary>
        private void ReadMousePos()
        {
            //Offset MouseReading to picPanel control
            MousePosition.Offset(picPanel.Left, picPanel.Top);

            //New position
            double x = offX - MousePosition.X;
            double y = offY - MousePosition.Y;

            //Scaling
            _movement.X = ((x) / (picPanel.Width / 16));
            _movement.Y = ((y) / (picPanel.Height / 16));

        }

        /// <summary>
        /// Updates UI two labels and the position of the ball picture
        /// </summary>
        /// <param name="turn">Amount of turn</param>
        /// <param name="speed">Amount of speed</param>
        private void UpdateGraphic(double turn, double speed)
        {
            if (!InvokeRequired)
            {
                //Places the ball compared to speed and turn
                picBall.Left = (int)Math.Round(((8 - turn) * offsetY) - (picBall.Height / 2) + picPanel.Left, 0);
                picBall.Top = (int)Math.Round(((8 - speed) * offsetX) - (picBall.Width / 2) + picPanel.Top, 0);

                //Update labels with new values
                lblX.Text = "Byte:" + Convert.ToString(_robotController.LastSendDataByte, 2);
                lblY.Text = String.Format("Speed:{0} Turn:{1}", Math.Round(speed, 0), Math.Round(-turn, 0));

            }
            else
                Invoke(new DelegateForUIUpdate(UpdateGraphic), new object[] { turn, speed });

        }

        /// <summary>
        /// Draws the background, the square and two lines of picPanel
        /// </summary>
        private void DrawBG()
        {
            int width = picPanel.Size.Width;
            int height = picPanel.Size.Height;

            Pen blackPen = new Pen(Color.Black);
            Pen whitePen = new Pen(Color.White);
            Brush whiteBrush = new SolidBrush(Color.White);

            Bitmap bmp = new Bitmap(width, height);

            Graphics gfx = Graphics.FromImage(bmp);
            gfx.FillRectangle(whiteBrush, new Rectangle(0, 0, width, height));
            gfx.DrawLine(blackPen, 0, height / 2, width, height / 2);
            gfx.DrawLine(blackPen, width / 2, 0, width / 2, height);
            gfx.DrawRectangle(blackPen, new Rectangle(0, 0, width - 1, height - 1));

            picPanel.Image = bmp;
            picPanel.Invalidate();

            gfx.Dispose();
        }


        #endregion

        #region Event handlers

        #region button events

        void btnGSensor_Click(object sender, EventArgs e)
        {
            if (_gSensorEnable)
            {
                timer1.Enabled = false;
                _gSensorEnable = false;
                btnGSensor.Text = "Start G Sensor";

                picBall.MouseDown += new MouseEventHandler(picBall_MouseDown);
                picBall.MouseUp += new MouseEventHandler(picBall_MouseUp);

                _movement.X = 0;
                _movement.Y = 0;

                _robotController.MoveRobot((int)Math.Round(_movement.Y, 0), (int)Math.Round(_movement.X, 0));
                UpdateGraphic(_movement.X, _movement.Y);
            }
            else
            {
                _gOffset = _gSensor.GetGVector();
                _gOffset.Scale(SCALE_FACTOR);

                picBall.MouseDown -= picBall_MouseDown;
                picBall.MouseUp -= picBall_MouseUp;

                timer1.Enabled = true;
                _gSensorEnable = true;
                
                btnGSensor.Text = "Stop G Sensor";
            }
        }

        void btnCom_Click(object sender, EventArgs e)
        {
            try
            {
                if (btnCom.Text.CompareTo("Connect") == 0)
                {
                    tbStatus.Text += "BT Connecting..." + Environment.NewLine;

                    _robotController.PortName = (string)(cbComPort.SelectedItem);
                    if (_robotController.ConnectBlueTooth())
                    {
                        btnCom.Text = "Disconnect";
                        tbStatus.Text += "BT Connected" + Environment.NewLine;
                    }
                    else
                    {
                        tbStatus.Text += "BT Not connected" + Environment.NewLine;
                    }
                }
                else
                {
                    tbStatus.Text += "BT Disconnecting..." + Environment.NewLine;
                    _robotController.DisconnectBlueTooth();
                    btnCom.Text = "Connect";
                    tbStatus.Text += "BT Disconnected" + Environment.NewLine;
                }
            }
            catch (Exception ex)
            {
                tbStatus.Text += "Something went wrong!" + Environment.NewLine + ex.Message + Environment.NewLine;
            }
        }

        #endregion

        #region mouse events
    
        void picBall_MouseUp(object sender, MouseEventArgs e)
        {
            timer1.Enabled = false;
            
            picBall.Left = (picPanel.Size.Width / 2) - (picBall.Width / 2) + picPanel.Left;
            picBall.Top = (picPanel.Size.Height / 2) - (picBall.Height / 2) + picPanel.Top;

            _movement.X = 0;
            _movement.Y = 0;

            _robotController.MoveRobot((int)Math.Round(_movement.Y, 0), (int)Math.Round(_movement.X, 0));
            UpdateGraphic(_movement.X, _movement.Y);
        }

        void picBall_MouseDown(object sender, MouseEventArgs e)
        {
            MousePosition.Offset(picPanel.Left, picPanel.Top);

            offX = MousePosition.X;
            offY = MousePosition.Y;

            timer1.Enabled = true;
        }

        #endregion

        #region timer tick event
        /// <summary>
        /// Timer event handler
        /// reads either GSensor og MousePosition and sends new data to
        /// RobotController and updates UI
        /// </summary>
        private void timer1_Tick(object sender, EventArgs e)
        {
            //Checks whether it's G sensor or mouse in use
            if (_gSensorEnable)
                ReadGSensor();
            else
                ReadMousePos();

            //Boundry cutting
            _movement.X = _movement.X > 7 ? 7 : _movement.X;
            _movement.X = _movement.X < -7 ? -7 : _movement.X;
            _movement.Y = _movement.Y > 7 ? 7 : _movement.Y;
            _movement.Y = _movement.Y < -7 ? -7 : _movement.Y;

            //Move robot
            _robotController.MoveRobot((int)Math.Round(_movement.X, 0), (int)Math.Round(_movement.Y, 0));

            //Update UI with new values
            UpdateGraphic(_movement.X, _movement.Y);
        }
        #endregion
        #endregion

    }


    
}
